#ifndef _SRC_NET_TCP_CLIENT_H_
#define _SRC_NET_TCP_CLIENT_H_

#include <memory>
#include <mutex>
#include <string>

#include "src/net/tcp_connection.h"
#include "src/base/utils.h"

namespace fio66::net {
class Connector;
typedef std::shared_ptr<Connector> ConnectorPtr;

class TcpClient {
 public:
  DISALLOW_COPY_AND_ASSIGN(TcpClient)
  // TcpClient(EventLoop* loop);
  // TcpClient(EventLoop* loop, const std::string& host, uint16_t port);
  TcpClient(EventLoop* loop, const InetAddress& serverAddr,
            const std::string& nameArg);
  ~TcpClient();  // force out-line dtor, for std::unique_ptr members.

  void Connect();
  void Disconnect();
  void Stop();

  TcpConnectionPtr Connection() {
    std::lock_guard lock(mutex_);
    return connection_;
  }

  EventLoop* GetLoop() const { return loop_; }
  bool Retry() const { return retry_; }
  void EnableRetry() { retry_ = true; }

  const std::string& Name() const { return name_; }

  /// Set Connection callback.
  /// Not thread safe.
  void SetConnectionCallback(ConnectionCallback cb) {
    connection_callback_ = std::move(cb);
  }

  /// Set message callback.
  /// Not thread safe.
  void SetMessageCallback(MessageCallback cb) {
    message_callback_ = std::move(cb);
  }

  /// Set write complete callback.
  /// Not thread safe.
  void SetWriteCompleteCallback(WriteCompleteCallback cb) {
    write_complete_callback_ = std::move(cb);
  }

 private:
  /// Not thread safe, but in loop
  void NewConnection(int sockfd);
  /// Not thread safe, but in loop
  void RemoveConnection(const TcpConnectionPtr& conn);

  EventLoop* loop_;
  ConnectorPtr connector_;  // avoid revealing Connector
  const std::string name_;
  ConnectionCallback connection_callback_;
  MessageCallback message_callback_;
  WriteCompleteCallback write_complete_callback_;
  bool retry_;    // atomic
  bool connect_;  // atomic
  // always in loop thread
  int next_conn_id_;
  std::mutex mutex_;
  TcpConnectionPtr connection_;
};

}  // namespace fio66::net

#endif  // _SRC_NET_TCP_CLIENT_H_
